package com.open.capacity.point.service.impl;

import com.open.capacity.point.constant.BusinessConstant;
import com.open.capacity.point.constant.PointsRuleTypeConstant;
import com.open.capacity.point.dao.PointsLogDao;
import com.open.capacity.point.dto.DateQuery;
import com.open.capacity.point.feign.UserFeignClient;
import com.open.capacity.point.service.PointsRuleService;
import com.open.capacity.point.service.PointsService;
import cn.hutool.core.util.StrUtil;
import com.open.capacity.common.constant.PointsRuleConstant;
import com.open.capacity.common.exception.service.ServiceException;
import com.open.capacity.common.model.PointsLog;
import com.open.capacity.common.model.PointsRule;
import com.open.capacity.common.model.SysUser;
import com.open.capacity.common.web.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Objects;

@Service
@Slf4j
public class PointsServiceImpl implements PointsService {

    @Resource
    private PointsLogDao pointsLogDao;
    @Resource
    private PointsRuleService pointsRuleService;
    @Resource
    private UserFeignClient userFeignClient;

    @Override
    public Integer findCurrentPoints(SysUser user) {
        int points = 0;
        if (Objects.nonNull(user)) {
            if (!StrUtil.hasBlank(user.getUsername())) {
                points = pointsLogDao.selectCurrentPointsByUsername(user.getUsername());
            } else if (Objects.nonNull(user.getId())) {
                points = pointsLogDao.selectCurrentPointsByUserId(user.getId());
            }
        }
        return points;
    }

    @Override
    public Result findObtainPoints(Long userId) throws ServiceException {
        try {
            Integer points = findObtainPointsByDate(userId, null);
            return Result.succeed(points, BusinessConstant.Points.OATAIN_POINTS);
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    @Override
    public Result findTodayObtainPoints(Long userId) throws ServiceException {
        try {
            DateQuery dateQuery = DateQuery.builder().startDate(new Date()).build();
            Integer points = findObtainPointsByDate(userId, dateQuery);
            return Result.succeed(points, BusinessConstant.Points.OBTAIN_POINTS_OF_TODAY);
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    @Override
    public Result findConsumePoints(Long userId) throws ServiceException {
        try {
            Integer points = findConsumePointsByDate(userId, null);
            return Result.succeed(points, BusinessConstant.Points.CONSUME_POINTS);
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    @Override
    public Result findTodayConsume(Long userId) throws ServiceException {
        try {
            DateQuery dateQuery = DateQuery.builder().startDate(new Date()).build();
            Integer points = findConsumePointsByDate(userId, dateQuery);
            return Result.succeed(points, BusinessConstant.Points.CONSUME_POINTS_OF_TODAY);
        } catch (Exception e) {
            throw new ServiceException(e);
        }
    }

    @Override
    public Integer findObtainPointsByDate(Long userId, DateQuery dateQuery) {
        Integer result = pointsLogDao.selectPointsByCondition(userId, PointsRuleTypeConstant.OBTAIN, dateQuery);
        return result == null ? 0 : result;
    }

    @Override
    public Integer findConsumePointsByDate(Long userId, DateQuery dateQuery) {
        Integer result = pointsLogDao.selectPointsByCondition(userId, PointsRuleTypeConstant.CONSUME, dateQuery);
        return result == null ? 0 : result;
    }

    @Override
    @Transactional
    public Result change(Long userId, String pointsRuleCode, int points) throws ServiceException {
        try {
            PointsRule pointsRule = pointsRuleService.findRuleByCode(pointsRuleCode);  // 触发的积分规则
            Integer id = pointsRule.getId();       // 积分规则id
            String pointType = pointsRule.getPointType();        // 积分规则类型 （限制、消费、获得）
            Integer rulePoints = pointsRule.getPoints();   // 积分规则对应的积分值
            // 获取当前积分
            Integer currentPoints = pointsLogDao.selectCurrentPointsByUserId(userId);
            // 用户未产生过积分变动，获得默认初始积分
            if (currentPoints == null) {
                PointsRule initRule = pointsRuleService.findRuleByCode(PointsRuleConstant.INIT);
                currentPoints = initRule.getPoints();
                PointsLog pointsLog = PointsLog.builder().id(initRule.getId()).points(currentPoints).userId(userId).build();
                pointsLogDao.insert(pointsLog);
            }
            points = points < 0 ? 0 : points;
            // 判断积分规则类型
            if (pointType.equals(PointsRuleTypeConstant.OBTAIN)) {
                // 积分值校验
                int limitPoints = pointsRuleService.findRuleByCode(PointsRuleConstant.POINTS_LIMIT_OF_DAY).getPoints();  // 查询每日获取积分限制
                Integer todayObtainPoints = (Integer) findTodayObtainPoints(userId).getData();                   // 查询今日获得积分
                if (todayObtainPoints >= limitPoints) return Result.failed(BusinessConstant.Points.OBTAIN_POINTS_LIMIT_OF_TODAY);
                points += currentPoints + rulePoints;
            } else if (pointType.equals(PointsRuleTypeConstant.CONSUME)) {
                points = currentPoints - (points + rulePoints);
                if (points < 0) return Result.failed(BusinessConstant.Points.INSUFFICIENT_POINTS);
            }
            PointsLog pointsLog = PointsLog.builder().id(id).points(points).userId(userId).build();
            int isChange = pointsLogDao.insert(pointsLog);
            // 积分变动后检查是否触发称号规则
            if (isChange > 0) {
                Integer obtainPoints = (Integer) findObtainPoints(userId).getData();// 查询用户累计获得积分
                userFeignClient.updateTitleByUserIdAndPoints(userId, obtainPoints.doubleValue(), "POINT");
            }
        } catch (Exception e) {
            log.warn(e.getMessage());
            throw new ServiceException(e);
        }
        return Result.succeed(points, BusinessConstant.Points.POINTS_CHANGE);
    }
}
